#ifndef NETCLIENT_H
#define NETCLIENT_H

#include"../model/data.h"

#include <QObject>
#include<QNetworkAccessManager>
#include<QWebSocket>
#include<QProtobufSerializer>
#include<QNetworkReply>


//此处为了避免头文件"循环包含"问题,就需要使用前置声明代替包含头文件
namespace model{
    class DataCenter;
}//end model


namespace network{

class NetClient : public QObject
{
    Q_OBJECT
private:
    //定义重要常量、ip暂时使用本地的回环ip、端口号约定成8000和8001;
    const QString HTTP_URL = "http://127.0.0.1:8000";
    const QString WEBSOCKET_URL = "ws://127.0.0.1:8001/ws";

public:
    NetClient(model::DataCenter* dataCenter);

    //验证网络联通
    void ping();

    //初始化websocket
    void initWebsocket();

    //针对websocket的处理
    void handleWsResponse(const bite_im::NotifyMessage& notifyMessage);
    void handleWsMessage(const model::Message& message);
    void handleWsRemoveFriend(const QString& userId);
    void handleWsAddFriendApply(const model::UserInfo& userInfo);
    void handleWsAddFriendProcess(const model::UserInfo& userInfo,bool agree);
    void handleWsSessionCreate(const model::ChatSessionInfo& chatSessionInfo);

    //发送身份认证请求
    void sendAuth();

    //生成请求id
    const QString makeRequestId();

    //封装发送请求的逻辑
    QNetworkReply* sendHttpRequest(const QString& apiPath,const QByteArray& body);

    //封装处理响应的逻辑(包括HTTP正确性，反序列化，判定业务上的正确性)
    //由于不同的api,返回的pb对象结构不同，为了让一个函数处理多种不同类，需要使用模版
    //通过输出型参数，表达这次操作是成功还是失败的，以及失败的原因
    template<typename T>
    std::shared_ptr<T> handleHttpResponse(QNetworkReply* httpResp,bool* ok,QString* reason){
        //1.判定HTTP层面上是否出错
        if(httpResp->error() != QNetworkReply::NoError){
            *ok = false;
            *reason = httpResp->errorString();
            httpResp->deleteLater();
            return std::shared_ptr<T>();
        }

        //2.获取响应的body
        QByteArray respBody = httpResp->readAll();

        //3.针对body反序列化
        std::shared_ptr<T> respObj = std::make_shared<T>();
        respObj->deserialize(&serializer,respBody);

        //4.判定业务上的结果是否出错
        if(!respObj->success()){
            *ok = false;
            *reason = respObj->errmsg();
            httpResp->deleteLater();
            return std::shared_ptr<T>();
        }

        //5.释放httpResp对象
        httpResp->deleteLater();
        *ok = true;
        return respObj;
    }

    //网络通信获取用户个人信息
    void getMyself(const QString& loginSessionId);
    //网络通信获取用户好友列表
    void getFriendList(const QString& loginSessionId);
    //网络通信获取用户会话列表
    void getChatSessionList(const QString& loginSessionId);
    //网络通信获取用户好友申请列表
    void getApplyList(const QString& loginSessionId);
    //网络通信获取用户最近消息列表
    void getRecentMessageList(const QString& loginSessionId,const QString& chatSessionId,bool updateUI);
    //网络通信客户端将信息发送给服务端
    void sendMessage(const QString& loginSessionId,const QString& chatSessionId,model::MessageType messageType,
                     const QByteArray& content,const QString& extraInfo);
    //网络通信收到信息
    void receiveMessage(const QString& chatSessionId);
    //网络通信修改昵称
    void changeNickname(const QString& loginSessionId,const QString& nickname);
    //网络通信修改签名
    void changeDescription(const QString& loginSessionId,const QString& desc);
    //网络通信获取验证码
    void getVerifyCode(const QString& phone);
    //网络通信修改电话
    void changePhone(const QString& loginSessionId,const QString& phone,const QString& verifyCodeId,const QString& verifyCode);
    //网络通信修改头像
    void changeAvatar(const QString& loginSessionId,const QByteArray& avatar);
    //网络通信删除好友
    void deleteFriend(const QString& loginSessionId,const QString& userId);
    //网络通信发送好友申请
    void addFriendApply(const QString& loginSessionId,const QString& userId);
    //网络通信同意对方的好友申请
    void acceptFriendApply(const QString& loginSessionId,const QString& userId);
    //网络通信拒绝对方的好友申请
    void rejectFriendApply(const QString& loginSessionId,const QString& userId);
    //网络通信创建群聊
    void createGroupChatSession(const QString& loginSessionId,const QList<QString>& userIdList);
    //网络通信获取群聊成员列表
    void getMemberList(const QString& loginSessionId,const QString& chatSessionId);
    //网络通信修改群聊昵称 TODO
    void changeGroupName(const QString& loginSessionId,const QString& chatSessionId,const QString& groupName);
    //网络通信搜索用户
    void searchUser(const QString& loginSessionId,const QString& searchkey);
    //网路通信按关键词搜索历史消息
    void searchMessageByKey(const QString& loginSessionId,const QString& chatSessionId, const QString& searchKey);
    //网路通信按时间搜索历史消息
    void searchMessageByTime(const QString& loginSessionId,const QString& chatSessionId, const QDateTime& begTime,const QDateTime& endTime);
    //网络通信用户名登录
    void userLogin(const QString& username,const QString& password);
    //网络通信用户名注册
    void userRegister(const QString& username,const QString& password);
    //网络通信电话号码登录
    void phoneLogin(const QString& phone,const QString& verifyCodeId,const QString& verifyCode);
    //网络通信电话号码注册
    void phoneRegister(const QString& phone,const QString& verifyCodeId,const QString& verifyCode);
    //网络通信获取单个文件
    void getSingleFile(const QString& loginSessionId,const QString& fileId);
    //网络通信语音转文字
    void speechConvertText(const QString& loginSessionId,const QString& fileId,const QByteArray& content);

private:
    model::DataCenter* dataCenter;

    //http客户端
    QNetworkAccessManager httpClient;

    //websocket客户端
    QWebSocket websocketClient;

    //序列化器
    QProtobufSerializer serializer;

signals:
};
}//end network
#endif // NETCLIENT_H
